J'écris un plugin qui fait en sorte que les recherches ne fonctionnent que sur un type de publication personnalisé particulier et trie les résultats en fonction des taxonomies que l'utilisateur sélectionne dans sa configuration de base.
L'administrateur peut sélectionner jusqu'à 4 taxonomies du type de publication personnalisé. Chacune est alors un critère de tri qui doit être appliqué aux résultats de la recherche. L'administrateur peut ensuite sélectionner les termes de chaque taxonomie qui font que le message apparaisse avant les autres dans les résultats de recherche. La première taxonomie sélectionnée sera le critère de tri le plus important; les autres seront appliqués dans l'ordre.
J'ai besoin d'une requête dans les lignes de:
SELECT * FROM wp_posts ORDER BY choosen_terms_of_taxonomy1, choosen_terms_of_axonomy2, ...
Utilisons maintenant quelques exemples de données. Nous avons le type de poste course
:
+----+------------+
| ID | post_title |
+----+------------+
| 12 | Cooking |
+----+------------+
| 13 | Surfing |
+----+------------+
| 14 | Building |
+----+------------+
| 15 | Hacking |
+----+------------+
Ensuite, nous avons deux taxonomies pour ce type de message personnalisé. L'un est place
et l'autre est pricetag
. La taxonomie place
a les termes suivants:
+---------+------------+
| term_id | slug |
+---------+------------+
| 21 | downtown |
+---------+------------+
| 22 | abroad |
+---------+------------+
La taxonomie pricetag
a les termes suivants:
+---------+------------+
| term_id | slug |
+---------+------------+
| 31 | expensive |
+---------+------------+
| 32 | cheap |
+---------+------------+
Et finalement nous avons wp_term_relationships
qui relie les cours aux termes de taxonomie de cette façon:
+-----------+------------+---------+
| 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)
+-----------+------------+---------+
(Remarque: je sais que ce n'est pas la structure réelle de la table wp_term_relationships
, mais je voulais la simplifier un peu pour les besoins de cette question.)
Supposons que l’administrateur ait choisi place
comme première taxonomie à utiliser comme critère de tri et qu’il ait choisi d’afficher les cours downtown
en premier (l’écran d’administration du plug-in est déjà terminé et il fournit déjà à l’administrateur l’interface utilisateur nécessaire pour effectuer de tels choix. ).
Dites ensuite que l’administrateur a choisi pricetag
comme seconde taxonomie à utiliser comme critère de tri et qu’il souhaite que expensive
cours apparaisse en premier. Notez qu’il s’agit d’un second critère, il a une priorité de tri moins importante que le premier. Par conséquent, l’administrateur souhaite commencer par les cours du centre-ville, puis par les cours coûteux du groupe des cours du centre-ville.
Maintenant, un utilisateur frontal recherche tous les cours sur le site Web et il devrait voir ces résultats dans cet ordre exact:
Mon problème est d'écrire les clauses JOIN
et ORDER BY
correctes dans l'objet de requête Wordpress. J'ai déjà accroché posts_clauses
et voici la requête SQL que je génère:
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
Cependant, cette requête a au moins deux problèmes:
post_date DESC
), mais il est clair que ce n'est PAS ce à quoi je m'attendais.J'ai essayé de simplifier la requête en supprimant les clauses générées par Wordpress:
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
Celui-ci a exactement les mêmes problèmes, mais il est un peu plus facile à comprendre et renvoie les données comme si aucun ORDER BY
n'y était présent.
Pouvez-vous m'aider s'il vous plaît?
Malheureusement, bien que WP_Query
prenne en charge le 'tax_query'
arg, il ne prend pas en charge les commandes basées sur les conditions de publication. Vous devrez donc modifier la requête SQL, comme vous le faites maintenant. Cependant, vous construisez la clause ORDER BY
de manière incorrecte, raison pour laquelle elle commande par post_date
. Ce que vous devez faire, c'est utiliser une instruction CASE
, comme ceci:
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
Cela ordonnera en fonction de la priorité que vous attribuez à chacun des termes (1
, 0
, etc., la priorité étant la priorité la plus élevée, sauf si vous utilisez ASC
au lieu de DESC
pour la commande).
Parce que vous voulez commander ces deux taxonomies indépendamment, vous devez avoir deux jointures et deux déclarations de cas. (Voir ci-dessous par exemple.)
Vous devez également créer un GROUP BY
sur l'ID de l'article pour éviter les doublons:
$clauses['groupby'] = 'wptests_posts.ID';
Ainsi, votre requête finale ressemblera à quelque chose comme ceci (formaté pour en faciliter la lecture):
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
Voici un exemple de test PHPUnit qui montre que cela fonctionne, y compris un exemple de code permettant de générer les jointures et orderbys (utilisé pour générer la requête ci-dessus):
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;
}
}
Vous pouvez utiliser tax_query
pour trier avec la taxonomie i.e.
$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 );