it-swarm.com.de

So führen Sie zwei Abfragen zusammen

Ich versuche, die Beiträge in einer Kategorie zu ordnen, indem ich zuerst die Beiträge mit Bildern und zuletzt die Beiträge ohne Bilder anzeige. Ich habe es geschafft, indem ich zwei Abfragen ausgeführt habe, und jetzt möchte ich die beiden Abfragen zusammenführen.

Ich habe folgendes:

<?php
$loop = new WP_Query( array('meta_key' => '_thumbnail_id', 'cat' => 1 ) );
$loop2 = new WP_Query( array('meta_key' => '', 'cat' => 1 ) );
$mergedloops = array_merge($loop, $loop2);

while($mergedloops->have_posts()): $mergedloops->the_post(); ?>

Aber wenn ich versuche, die Seite anzuzeigen, erhalte ich die folgende Fehlermeldung:

 Fatal error: Call to a member function have_posts() on a non-object in...

Ich habe dann versucht, array_merge auf ein Objekt zu übertragen, habe jedoch den folgenden Fehler erhalten:

Fatal error: Call to undefined method stdClass::have_posts() in...

Wie kann ich diesen Fehler beheben?

10
Howli

Eine einzelne Abfrage

Denken Sie ein bisschen mehr darüber nach und es besteht die Möglichkeit, dass Sie mit einer einzelnen/der Hauptabfrage gehen können. Oder mit anderen Worten: Sie benötigen keine zwei zusätzlichen Abfragen, wenn Sie mit der Standardabfrage arbeiten können. Und falls Sie nicht mit einer Standardabfrage arbeiten können, benötigen Sie nicht mehr als eine Abfrage, unabhängig davon, wie viele Schleifen Sie die Abfrage teilen möchten.

Voraussetzungen

Zuerst müssen Sie (wie in meiner anderen Antwort gezeigt) die erforderlichen Werte in einem pre_get_posts-Filter einstellen. Dort setzen Sie wahrscheinlich posts_per_page und cat. Beispiel ohne den pre_get_posts- Filter:

$catID = 1;
$catQuery = new WP_Query( array(
    'posts_per_page' => -1,
    'cat'            => $catID,
) );
// Add a headline:
printf( '<h1>%s</h1>', number_format_i18n( $catQuery->found_posts )
    .__( " Posts filed under ", 'YourTextdomain' )
    .get_cat_name( $catID ) );

Eine Basis aufbauen

Das nächste, was wir brauchen, ist ein kleines benutzerdefiniertes Plugin (oder fügen Sie es einfach in Ihre functions.php-Datei ein, wenn es Ihnen nichts ausmacht, es während Updates oder Themenänderungen zu verschieben):

<?php
/**
 * Plugin Name: (#130009) Merge Two Queries
 * Description: "Merges" two queries by using a <code>RecursiveFilterIterator</code> to divide one main query into two queries
 * Plugin URl:  http://wordpress.stackexchange.com/questions/130009/how-to-merge-two-queries-together
 */

class ThumbnailFilter extends FilterIterator implements Countable
{
    private $wp_query;

    private $allowed;

    private $counter = 0;

    public function __construct( Iterator $iterator, WP_Query $wp_query )
    {
        NULL === $this->wp_query AND $this->wp_query = $wp_query;

        // Save some processing time by saving it once
        NULL === $this->allowed
            AND $this->allowed = $this->wp_query->have_posts();

        parent::__construct( $iterator );
    }

    public function accept()
    {
        if (
            ! $this->allowed
            OR ! $this->current() instanceof WP_Post
        )
            return FALSE;

        // Switch index, Setup post data, etc.
        $this->wp_query->the_post();

        // Last WP_Post reached: Setup WP_Query for next loop
        $this->wp_query->current_post === $this->wp_query->query_vars['posts_per_page'] -1
            AND $this->wp_query->rewind_posts();

        // Doesn't meet criteria? Abort.
        if ( $this->deny() )
            return FALSE;

        $this->counter++;
        return TRUE;
    }

    public function deny()
    {
        return ! has_post_thumbnail( $this->current()->ID );
    }

    public function count()
    {
        return $this->counter;
    }
}

Dieses Plugin macht eine Sache: Es benutzt die PHP SPL (Standard PHP Library) und ihre Interfaces und Iteratoren. Was wir jetzt haben, ist eine FilterIterator, mit der wir Gegenstände bequem aus unserer Schleife entfernen können. Es erweitert den PHP SPL Filter Iterator, damit wir nicht alles einstellen müssen. Der Code ist gut kommentiert, aber hier sind einige Hinweise:

  1. Die Methode accept() ermöglicht das Definieren von Kriterien, die das Schleifen des Elements ermöglichen - oder nicht.
  2. In dieser Methode verwenden wir WP_Query::the_post(), sodass Sie einfach jedes Template-Tag in Ihrer Template-Dateischleife verwenden können.
  3. Und wir überwachen auch die Schleife und spulen die Posts zurück, wenn wir den letzten Punkt erreichen. Dies ermöglicht es, eine unendliche Anzahl von Schleifen zu durchlaufen, ohne unsere Abfrage zurückzusetzen.
  4. Es gibt eine benutzerdefinierte Methode, die nicht Teil der FilterIterator-Spezifikation ist: deny(). Diese Methode ist besonders praktisch, da sie nur unsere "process or not" -Anweisung enthält und wir sie in späteren Klassen leicht überschreiben können, ohne dass wir etwas anderes als WordPress-Template-Tags wissen müssen.

Wie schleife ich?

Mit diesem neuen Iterator brauchen wir nicht mehr if ( $customQuery->have_posts() ) und while ( $customQuery->have_posts() ). Wir können mit einer einfachen foreach-Anweisung fortfahren, da alle erforderlichen Überprüfungen bereits für uns durchgeführt wurden. Beispiel:

global $wp_query;
// First we need an ArrayObject made out of the actual posts
$arrayObj = new ArrayObject( $wp_query->get_posts() );
// Then we need to throw it into our new custom Filter Iterator
// We pass the $wp_query object in as second argument to keep track with it
$primaryQuery = new ThumbnailFilter( $arrayObj->getIterator(), $wp_query );

Schließlich brauchen wir nichts weiter als eine foreach Standardschleife. Wir können sogar the_post() löschen und trotzdem alle Template-Tags verwenden. Das globale $post-Objekt bleibt immer synchron.

foreach ( $primaryQuery as $post )
{
    var_dump( get_the_ID() );
}

Nebenschleifen

Das Schöne daran ist, dass jeder spätere Abfragefilter ganz einfach zu handhaben ist: Definieren Sie einfach die Methode deny(), und schon können Sie Ihre nächste Schleife beginnen. $this->current() verweist immer auf unseren aktuell geloopten Beitrag.

class NoThumbnailFilter extends ThumbnailFilter
{
    public function deny()
    {
        return has_post_thumbnail( $this->current()->ID );
    }
}

Da wir definiert haben, dass wir jetzt deny() jeden Beitrag mit einer Miniaturansicht durchlaufen, können wir sofort alle Beiträge ohne Miniaturansicht durchlaufen:

foreach ( $secondaryQuery as $post )
{
    var_dump( get_the_title( get_the_ID() ) );
}

Probier es aus.

Das folgende Test Plugin ist als Gist auf GitHub verfügbar. Einfach hochladen und aktivieren. Es gibt die ID jedes geloopten Posts als Rückruf für die Aktion loop_start aus bzw. gibt sie aus. Dies bedeutet, dass je nach Setup, Anzahl der Posts und Konfiguration möglicherweise einiges ausgegeben wird. Bitte fügen Sie einige abort-Anweisungen hinzu und ändern Sie die var_dump()s am Ende dahingehend, was Sie sehen möchten und wo Sie es sehen möchten. Es ist nur ein Proof of Concept.

8
kaiser

Dies ist zwar nicht die beste Lösung für dieses Problem (die Antwort von @ kaiser lautet), aber um die Frage direkt zu beantworten, werden die tatsächlichen Abfrageergebnisse in $loop->posts und $loop2->posts angezeigt.

$mergedloops = array_merge($loop->posts, $loop2->posts);

... sollte funktionieren, aber Sie müssten eine foreach-Schleife und nicht die WP_Query-basierte Standardschleifenstruktur verwenden, da das Zusammenführen solcher Abfragen die "Metadaten" des WP_Query-Objekts über die Schleife zerstört.

Sie können dies auch tun:

$loop = new WP_Query( array('fields' => 'ids','meta_key' => '_thumbnail_id', 'cat' => 1 ) );
$loop2 = new WP_Query( array('fields' => 'ids','meta_key' => '', 'cat' => 1 ) );
$ids = array_merge($loop->posts, $loop2->posts);
$merged = new WP_Query(array('post__in' => $ids,'orderby' => 'post__in'));

Natürlich stellen diese Lösungen mehrere Abfragen dar, weshalb @ Kaiser's der bessere Ansatz für Fälle wie diesen ist, in denen WP_Query die erforderliche Logik verarbeiten kann.

6
s_ha_dum

Was Sie brauchen, ist eine dritte Abfrage, um alle Beiträge auf einmal zu erhalten. Anschließend ändern Sie Ihre ersten beiden Abfragen, sodass nicht die Beiträge, sondern nur die Beitrags-IDs in einem Format zurückgegeben werden, mit dem Sie arbeiten können.

Der Parameter 'fields'=>'ids' bewirkt, dass eine Abfrage tatsächlich ein Array mit übereinstimmenden Beitrags-ID-Nummern zurückgibt. Wir wollen jedoch nicht das gesamte Abfrageobjekt, sondern verwenden stattdessen get_posts.

Besorgen Sie sich zuerst die Post-IDs, die wir benötigen:

$imageposts = get_posts( array('fields'=>'ids', 'meta_key' => '_thumbnail_id', 'cat' => 1 ) );
$nonimageposts = get_posts( array('fields'=>'ids', 'meta_key' => '', 'cat' => 1 ) );

$ imageposts und $ nonimageposts sind nun beide eine Reihe von Beitrags-IDs, also führen wir sie zusammen

$mypostids = array_merge( $imageposts, $nonimageposts );

Beseitigen Sie die doppelten ID-Nummern ...

$mypostids = array_unique( $mypostids );

Stellen Sie nun eine Abfrage, um die tatsächlichen Beiträge in der angegebenen Reihenfolge abzurufen:

$loop = new WP_Query( array('post__in' => $mypostids, 'ignore_sticky_posts' => true, 'orderby' => 'post__in' ) );

Die Variable $ loop ist jetzt ein WP_Query-Objekt mit Ihren Beiträgen.

3
Otto

Tatsächlich gibt es meta_query (oder WP_Meta_Query ), das ein Array von Arrays annimmt, in denen Sie nach den _thumbnail_id-Zeilen suchen können. Wenn Sie dann nach EXISTS suchen, können Sie nur diejenigen abrufen, die dieses Feld haben. Wenn Sie dies mit dem Argument cat kombinieren, erhalten Sie nur Beiträge, die der Kategorie mit der ID 1 zugewiesen sind und an die ein Miniaturbild angehängt ist. Wenn Sie sie dann nach dem meta_value_num ordnen, ordnen Sie sie tatsächlich nach der niedrigsten bis höchsten Thumbnail-ID (wie bei order und ASC angegeben). Sie müssen die value nicht angeben, wenn Sie EXISTS als compare-Wert verwenden.

$thumbsUp = new WP_Query( array( 
    'cat'        => 1,
    'meta_query' => array( 
        array(
            'key'     => '_thumbnail_id',
            'compare' => 'EXISTS',
        ),
    ),
    'orderby'    => 'meta_value_num',
    'order'      => 'ASC',
) );

Wenn Sie sie nun durchlaufen, können Sie alle IDs sammeln und sie in einer exklusiven Anweisung für die untergeordnete Abfrage verwenden:

$postsWithThumbnails = array();
if ( $thumbsUp->have_posts() )
{
    while ( $thumbsUp->have_posts() )
    {
        $thumbsUp->the_post();

        // collect them
        $postsWithThumbnails[] = get_the_ID();

        // do display/rendering stuff here
    }
}

Jetzt können Sie Ihre zweite Abfrage hinzufügen. Hier ist wp_reset_postdata() nicht erforderlich - alles ist in der Variablen und nicht in der Hauptabfrage.

$noThumbnails = new WP_Query( array(
    'cat'          => 1,
    'post__not_in' => $postsWithThumbnails
) );
// Loop through this posts

Natürlich können Sie viel schlauer sein und einfach die SQL-Anweisung in pre_get_posts ändern, um die Hauptabfrage nicht zu verschwenden. Sie können auch einfach die erste Abfrage ($thumbsUp oben) in einem pre_get_posts-Filter-Rückruf durchführen.

add_filter( 'pre_get_posts', 'wpse130009excludeThumbsPosts' );
function wpse130009excludeThumbsPosts( $query )
{
    if ( $query->is_admin() )
        return $query;

    if ( ! $query->is_main_query() )
        return $query;

    if ( 'post' !== $query->get( 'post_type' ) )
        return $query;

    // Only needed if this query is for the category archive for cat 1
    if (
        $query->is_archive() 
        AND ! $query->is_category( 1 )
    )
        return $query;

    $query->set( 'meta_query', array( 
        array(
            'key'     => '_thumbnail_id',
            'compare' => 'EXISTS',
        ),
    ) );
    $query->set( 'orderby', 'meta_value_num' );

    // In case we're not on the cat = 1 category archive page, we need the following:
    $query->set( 'category__in', 1 );

    return $query;
}

Dadurch wurde die Hauptabfrage geändert, sodass wir nur Beiträge erhalten, an die ein Miniaturbild angehängt ist. Jetzt können wir (wie in der ersten Abfrage oben gezeigt) die IDs während der Hauptschleife sammeln und dann eine zweite Abfrage hinzufügen, die den Rest der Posts anzeigt (ohne Miniaturbild).

Außerdem können Sie den posts_clauses noch intelligenter ändern und die Abfrage direkt nach dem Metawert sortieren. Werfen Sie einen Blick auf diese Antwort , da die aktuelle nur ein Ausgangspunkt ist.

3
kaiser