it-swarm.com.de

Sortieren der Suchergebnisse nach taxonomischen Begriffen

Ich schreibe ein Plugin, mit dem die Suche nur für einen bestimmten benutzerdefinierten Beitragstyp funktioniert und das die Ergebnisse nach den Taxonomien sortiert, die der Benutzer in seiner Backend-Konfiguration auswählt.

Der Administrator kann bis zu 4 Taxonomien des benutzerdefinierten Beitragstyps auswählen. Jede Taxonomie ist dann ein Sortierkriterium, das auf die Suchergebnisse angewendet werden muss. Der Administrator kann dann die Begriffe jeder Taxonomie auswählen, die den Beitrag in den Suchergebnissen vor anderen erscheinen lassen. Die zuerst ausgewählte Taxonomie wird das wichtigste Sortierkriterium sein; Die anderen werden der Reihe nach angewendet.

Ich benötige eine Anfrage nach:

SELECT * FROM wp_posts ORDER BY choosen_terms_of_taxonomy1, choosen_terms_of_axonomy2, ...

Lassen Sie uns nun einige Beispieldaten verwenden. Wir haben den Post-Typ course:

+----+------------+
| ID | post_title |
+----+------------+
| 12 | Cooking    |
+----+------------+
| 13 | Surfing    |
+----+------------+
| 14 | Building   |
+----+------------+
| 15 | Hacking    |
+----+------------+

Dann haben wir zwei Taxonomien für diesen benutzerdefinierten Beitragstyp. Eine ist place und die andere ist pricetag. Die Taxonomie place hat die folgenden Begriffe:

+---------+------------+
| term_id |    slug    |
+---------+------------+
|      21 |  downtown  |
+---------+------------+
|      22 |  abroad    |
+---------+------------+

Die Taxonomie pricetag hat die folgenden Begriffe:

+---------+------------+
| term_id |    slug    |
+---------+------------+
|      31 |  expensive |
+---------+------------+
|      32 |  cheap     |
+---------+------------+

Und schließlich haben wir wp_term_relationships, der die Kurse auf diese Weise mit den Taxonomien verknüpft:

+-----------+------------+---------+
| object_id | post_title | term_id |
+-----------+------------+---------+
|        12 | Cooking    |      21 | (downtown)
+-----------+------------+---------+
|        12 | Cooking    |      32 | (cheap)
+-----------+------------+---------+
|        13 | Surfing    |      22 |    (abroad)
+-----------+------------+---------+
|        13 | Surfing    |      31 |    (expensive)
+-----------+------------+---------+
|        14 | Building   |      21 |       (downtown)
+-----------+------------+---------+
|        14 | Building   |      31 |       (expensive)
+-----------+------------+---------+
|        15 | Hacking    |      22 |          (abroad)
+-----------+------------+---------+
|        15 | Hacking    |      32 |          (cheap)
+-----------+------------+---------+

(Bitte beachten Sie: Ich weiß, dass dies nicht die eigentliche Struktur der wp_term_relationships-Tabelle ist, aber ich wollte sie aus Gründen dieser Frage ein wenig vereinfachen.)

Nehmen wir an, der Administrator hat place als erste Taxonomie ausgewählt, die als Sortierkriterium verwendet wird, und er hat ausgewählt, dass downtown Kurse zuerst angezeigt werden (der Administratorbildschirm des Plugins ist bereits fertig und der Administrator erhält bereits die Benutzeroberfläche, um solche Entscheidungen zu treffen ).

Sagen Sie dann, der Administrator hat pricetag als zweite Taxonomie ausgewählt, die als Sortierkriterium verwendet werden soll, und er möchte, dass expensive Kurse zuerst angezeigt werden. Beachten Sie, dass es als zweites Kriterium eine geringere Sortierpriorität als das erste Kriterium hat. Daher möchte der Administrator zuerst Kurse in der Innenstadt und dann in der Gruppe der Kurse in der Innenstadt zuerst teure Kurse.

Nun sucht ein Frontend-Benutzer nach allen Kursen auf der Website und sollte diese Ergebnisse in genau dieser Reihenfolge sehen:

  1. Baukurs (weil es in der Innenstadt und teuer ist)
  2. Kochkurs (weil es in der Innenstadt und billig ist)
  3. Surfkurs (weil es im Ausland und teuer ist)
  4. Hacking-Kurs (weil es im Ausland und billig ist)

Mein Problem besteht darin, die richtigen JOIN- und ORDER BY-Klauseln in das Wordpress-Abfrageobjekt zu schreiben. Ich habe posts_clauses bereits angehängt und hier ist die SQL-Abfrage, die ich generiere:

SELECT SQL_CALC_FOUND_ROWS  wp_posts.* FROM wp_posts 
  LEFT JOIN (wp_term_relationships, wp_term_taxonomy, wp_terms) 
    ON (wp_term_relationships.object_id = wp_posts.ID 
        AND wp_term_taxonomy.term_taxonomy_id = wp_term_relationships.term_taxonomy_id
        AND wp_terms.term_id = wp_term_taxonomy.term_id) 
  WHERE 1=1 AND (((wp_posts.post_title LIKE '%%') OR (wp_posts.post_excerpt LIKE '%%') OR (wp_posts.post_content LIKE '%%')))
            AND wp_posts.post_type IN 
                ('post', 'page', 'attachment', 'course') 
            AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private') 
            AND wp_posts.post_type='course' 
  ORDER BY (wp_terms.slug LIKE 'downtown' AND wp_term_taxonomy.taxonomy LIKE 'place') DESC,
           (wp_terms.slug LIKE 'abroad' AND wp_term_taxonomy.taxonomy LIKE 'place') DESC,
           (wp_terms.slug LIKE 'expensive' AND wp_term_taxonomy.taxonomy LIKE 'pricetag') DESC,
           (wp_terms.slug LIKE 'cheap' AND wp_term_taxonomy.taxonomy LIKE 'pricetag') DESC,
           wp_posts.post_title LIKE '%%' DESC, wp_posts.post_date DESC
 LIMIT 0, 300

Diese Abfrage weist jedoch mindestens zwei Probleme auf:

  1. es gibt das Doppelte der Ergebnisse zurück und ich verstehe nicht warum, da LEFT JOIN zwischen den angegebenen Tabellen kein kartesisches Produkt erzeugen sollte
  2. die resultierende Sortierreihenfolge ist mir unklar (es scheint nur post_date DESC zu sein), aber es ist klar, dass es NICHT das ist, was ich erwarte.

Ich habe versucht, die Abfrage zu vereinfachen, indem ich von Wordpress generierte Klauseln entferne:

SELECT wp_posts.* FROM wp_posts 
  LEFT JOIN (wp_term_relationships, wp_term_taxonomy, wp_terms) 
     ON (wp_term_relationships.object_id = wp_posts.ID 
        AND wp_term_taxonomy.term_taxonomy_id = wp_term_relationships.term_taxonomy_id
        AND wp_terms.term_id = wp_term_taxonomy.term_id) 
  WHERE 1=1 AND wp_posts.post_type='course' 
  ORDER BY (wp_terms.slug LIKE 'downtown' AND wp_term_taxonomy.taxonomy LIKE 'place') DESC,
       (wp_terms.slug LIKE 'abroad' AND wp_term_taxonomy.taxonomy LIKE 'place') DESC,
       (wp_terms.slug LIKE 'expensive' AND wp_term_taxonomy.taxonomy LIKE 'pricetag') DESC,
       (wp_terms.slug LIKE 'cheap' AND wp_term_taxonomy.taxonomy LIKE 'pricetag') DESC

Dieser hat genau die gleichen Probleme, ist aber etwas einfacher zu verstehen und gibt die Daten zurück, als ob überhaupt kein ORDER BY vorhanden wäre.

Kannst du mir bitte helfen?

6
Lucio Crusca

Obwohl WP_Query das Argument 'tax_query' unterstützt, unterstützt es leider keine Bestellung auf der Grundlage von Post-Terms. Sie müssen also die Abfrage-SQL ändern, wie Sie es jetzt tun. Sie konstruieren die ORDER BY-Klausel jedoch falsch, und aus diesem Grund wird nach post_date sortiert. Was Sie tun müssen, ist eine CASE-Anweisung wie folgt zu verwenden:

CASE 
    WHEN (wp_terms.slug LIKE 'downtown' AND wp_term_taxonomy.taxonomy LIKE 'place') THEN 1
    WHEN (wp_terms.slug LIKE 'abroad' AND wp_term_taxonomy.taxonomy LIKE 'place') THEN 0
END

Die Reihenfolge richtet sich nach der Priorität, die Sie den einzelnen Begriffen zuweisen (1, 0 usw., wobei eine höhere Priorität gilt, es sei denn, Sie verwenden ASC anstelle von DESC für die Bestellung).

Da Sie diese beiden Taxonomien unabhängig voneinander bestellen möchten, benötigen Sie zwei Joins und zwei case-Anweisungen. (Siehe unten zum Beispiel.)

Sie müssen auch einen GROUP BY auf der Beitrags-ID verursachen, um doppelte Ergebnisse zu vermeiden:

    $clauses['groupby'] = 'wptests_posts.ID';

Ihre endgültige Abfrage würde also ungefähr so ​​aussehen (zum leichteren Lesen formatiert):

   SELECT SQL_CALC_FOUND_ROWS  wptests_posts.ID FROM wptests_posts 
            LEFT JOIN (
                wptests_term_relationships tr_place,
                wptests_term_taxonomy tt_place,
                wptests_terms t_place
            ) ON (
                tr_place.object_id = wptests_posts.ID 
                AND tt_place.term_taxonomy_id = tr_place.term_taxonomy_id
                AND tt_place.taxonomy = 'place'
                AND t_place.term_id = tt_place.term_id
            ) 

            LEFT JOIN (
                wptests_term_relationships tr_pricetag,
                wptests_term_taxonomy tt_pricetag,
                wptests_terms t_pricetag
            ) ON (
                tr_pricetag.object_id = wptests_posts.ID 
                AND tt_pricetag.term_taxonomy_id = tr_pricetag.term_taxonomy_id
                AND tt_pricetag.taxonomy = 'pricetag'
                AND t_pricetag.term_id = tt_pricetag.term_id
            ) 
   WHERE 1=1  AND wptests_posts.post_type = 'course' AND (wptests_posts.post_status = 'publish')
   GROUP BY wptests_posts.ID
   ORDER BY 
        (CASE 
            WHEN (t_place.slug LIKE 'downtown') THEN 1
            WHEN (t_place.slug LIKE 'abroad') THEN 0
        END) DESC, (CASE
            WHEN (t_pricetag.slug LIKE 'expensive') THEN 1
            WHEN (t_pricetag.slug LIKE 'cheap') THEN 0
        END) DESC,
        wptests_posts.post_date DESC
   LIMIT 0, 10

Hier ist ein PHPUnit-Beispieltest, der zeigt, dass dies funktioniert, einschließlich Beispielcode zum Generieren der Joins und Orderbys (er wurde zum Generieren der obigen Abfrage verwendet):

class My_Test extends WP_UnitTestCase {

    public function test() {

        // Create the post type.
        register_post_type( 'course' );

        // Create the posts.
        $cooking_post_id = $this->factory->post->create(
            array( 'post_title' => 'Cooking', 'post_type' => 'course' )
        );
        $surfing_post_id = $this->factory->post->create(
            array( 'post_title' => 'Surfing', 'post_type' => 'course' )
        );
        $building_post_id = $this->factory->post->create(
            array( 'post_title' => 'Building', 'post_type' => 'course' )
        );
        $hacking_post_id = $this->factory->post->create(
            array( 'post_title' => 'Hacking', 'post_type' => 'course' )
        );

        // Create the taxonomies.
        register_taxonomy( 'place', 'course' );
        register_taxonomy( 'pricetag', 'course' );

        // Create the terms.
        $downtown_term_id = wp_create_term( 'downtown', 'place' );
        $abroad_term_id = wp_create_term( 'abroad', 'place' );

        $expensive_term_id = wp_create_term( 'expensive', 'pricetag' );
        $cheap_term_id = wp_create_term( 'cheap', 'pricetag' );

        // Give the terms to the correct posts.
        wp_add_object_terms( $cooking_post_id, $downtown_term_id, 'place' );
        wp_add_object_terms( $cooking_post_id, $cheap_term_id, 'pricetag' );

        wp_add_object_terms( $surfing_post_id, $abroad_term_id, 'place' );
        wp_add_object_terms( $surfing_post_id, $expensive_term_id, 'pricetag' );

        wp_add_object_terms( $building_post_id, $downtown_term_id, 'place' );
        wp_add_object_terms( $building_post_id, $expensive_term_id, 'pricetag' );

        wp_add_object_terms( $hacking_post_id, $abroad_term_id, 'place' );
        wp_add_object_terms( $hacking_post_id, $cheap_term_id, 'pricetag' );

        $query = new WP_Query(
            array(
                'fields'    => 'ids',
                'post_type' => 'course',
            )
        );

        add_filter( 'posts_clauses', array( $this, 'filter_post_clauses' ) );

        $results = $query->get_posts();

        $this->assertSame(
            array(
                $building_post_id,
                $cooking_post_id,
                $surfing_post_id,
                $hacking_post_id,
            )
            , $results
        );
    }

    public function filter_post_clauses( $clauses ) {

        global $wpdb;

        $clauses['orderby'] = "
            (CASE 
                WHEN (t_place.slug LIKE 'downtown') THEN 1
                WHEN (t_place.slug LIKE 'abroad') THEN 0
            END) DESC, (CASE
                WHEN (t_pricetag.slug LIKE 'expensive') THEN 1
                WHEN (t_pricetag.slug LIKE 'cheap') THEN 0
            END) DESC,
            " . $clauses['orderby'];

        foreach ( array( 'place', 'pricetag' ) as $taxonomy ) {

            // Instead of interpolating directly here, you should use $wpdb->prepare() for $taxonomy.
            $clauses['join'] .= "
                LEFT JOIN (
                    $wpdb->term_relationships tr_$taxonomy,
                    $wpdb->term_taxonomy tt_$taxonomy,
                    $wpdb->terms t_$taxonomy
                ) ON (
                    tr_$taxonomy.object_id = $wpdb->posts.ID 
                    AND tt_$taxonomy.term_taxonomy_id = tr_$taxonomy.term_taxonomy_id
                    AND tt_$taxonomy.taxonomy = '$taxonomy'
                    AND t_$taxonomy.term_id = tt_$taxonomy.term_id
                ) 
                ";
        }

        $clauses['groupby'] = 'wptests_posts.ID';

        return $clauses;
    }
}
5
J.D.

Sie können tax_query zum Sortieren mit Taxonomie verwenden, d. H.

  $args = array(
            posts_per_page => -1,
            post_type => 'your post type',
            tax_query' => array(
            'relation' => 'OR',
             array(
                  'taxonomy' => 'your custom taxonomy',
                  'field'    => 'slug',
                  'terms'    => $_REQUEST[your post requested],
                 ),
              array(
                    'taxonomy' => 'your 2nd custom taxonomy',
                    'field'    => 'slug',
                    'terms'    => $_REQUEST[your post requested],
                ),
            ),
        ):

       $query = new WP_Query( $args );
0
Abhishek Pandey