web-dev-qa-db-fra.com

Utilisation de meta_query sur pre_get_posts pour exclure une valeur particulière de meta_key

J'ai un filtre simple qui s'exécute sur la requête principale de la page d'accueil. Il devrait exclure toutes les publications ayant une méta-valeur particulière (en n'incluant que les publications pour lesquelles le paramètre meta_key n'est pas défini, ou celles dont la valeur meta_key ne correspond pas à ce que nous ' re excluant). Mais, pour une raison quelconque, il ne se comporte pas comme prévu.

/**
 * Hide posts from main query on front page.
 *
 * @since  1.0.0
 *
 * @param  object $query WP Query object.
 * @return object        Modified WP Query object.
 */
function wpse_exclude_posts_from_main_query( $query ) {

    // Make sure this only runs on the main query on the front page
    if ( is_front_page() && $query->is_main_query() ) {

        // Exclude posts that have been explicitly set to hidden
        $query->set('meta_query', array(
            'relation' => 'OR',
            // Include posts where the meta key isn't set
            array(
                'key'     => '_wpse_custom_key',
                'value'   => 'asdf', // A value must exist due to https://core.trac.wordpress.org/ticket/23268
                'compare' => 'NOT EXISTS',
            ),
            // Include posts where the meta key isn't explicitly true
            array(
                'key'     => '_wpse_custom_key',
                'value'   => true,
                'compare' => '!=',
            ),
        ) );

    }

}
add_action( 'pre_get_posts', 'wpse_exclude_posts_from_main_query' );

Chaque moitié de cette méta requête fonctionne parfaitement bien toute seule. Je peux voir toutes les publications où la clé n'existe pas ou toutes les publications où la clé existe et n'est pas vraie. Lorsqu'il est utilisé conjointement, comme indiqué ci-dessus, je ne vois que les publications où la clé existe et n'est pas vraie (la partie NOT EXISTS est complètement ignorée).

Voici le code SQL généré (selon le filtre posts_request):

SELECT     SQL_CALC_FOUND_ROWS wp_posts.*
FROM       wp_posts
LEFT JOIN  wp_postmeta
           ON (
               wp_posts.ID = wp_postmeta.post_id
               AND wp_postmeta.meta_key = '_wpse_custom_key'
           )
INNER JOIN wp_postmeta AS mt1
           ON (wp_posts.ID = mt1.post_id)
WHERE      1=1
           AND wp_posts.post_type = 'post'
           AND wp_posts.post_status = 'publish'
           AND (
               wp_postmeta.post_id IS NULL
               OR (
                   mt1.meta_key = '_wpse_custom_key'
                   AND CAST(mt1.meta_value AS CHAR) != '1'
               )
           )
GROUP BY   wp_posts.ID
ORDER BY   wp_posts.post_date DESC
LIMIT      0, 10

Vous pouvez voir que quand il s'agit des méta, il utilise bien OR dans la clause WHERE, plutôt que AND comme le bogue référencé le met en évidence: https://core.trac.wordpress.org/ticket/23268

J'espère que quelqu'un pourra vous fournir des informations indispensables, car je suis complètement déconcerté.

6
Brian Richards

Oui, ça se comporte bizarre. Non, tirez-moi, je ne peux pas savoir exactement pourquoi et comment (les jointures empilées le sont probablement).

Je dis le retourner. Les seuls messages que vous ne voulez pas voir sont ceux avec la clé personnalisée true. Interrogez séparément leurs ID, introduisez le résultat dans post__not_in, abandonnez entièrement cette méta-requête.

3
Rarst

Je me range aux côtés des utilisateurs de Twitter qui soupçonnaient le problème du code SQL composite. Je viens d'écrire une fonction différente pour exécuter mon propre SQL sur les posts_clauses, au lieu d'utiliser pre_get_posts, hook et les posts attendus sont correctement renvoyés.

Voici un exemple suivant les mêmes exigences que ci-dessus:

/**
 * Hide posts from main query on front page.
 *
 * @since  1.0.0
 *
 * @param  object $query WP Query object.
 * @return object        Modified WP Query object.
 */
function wpse_exclude_posts_clauses( $pieces, $query ) {

    // Make sure this only runs on the main query on the front page
    if ( is_front_page() && $query->is_main_query() ) {
        $pieces['join'] = "
            LEFT JOIN  $wpdb->postmeta as hidden_meta
                       ON (
                           $wpdb->posts.ID = hidden_meta.post_id
                           AND hidden_meta.meta_key = '_wpse_custom_key'
                       )
            ";
        $pieces['where'] = "
            AND (
                hidden_meta.post_id IS NULL
                OR CAST(hidden_meta.meta_value AS CHAR) != '1'
            )
            ";
    }

    return $pieces;
}
add_filter( 'posts_clauses', 'wpse_exclude_posts_clauses', 10, 2 );
2
Brian Richards

Je pense que votre problème est d’utiliser le vrai booléen comme valeur à exclure. J'ai changé la valeur d'exclusion en une chaîne, 'asdf' et j'ai changé la chaîne factice et cela a fonctionné.

La requête SQL a été copiée et collée à partir d'un vidage de l'objet global $ wp_query. La base de données a 5 postes comme on peut le voir à partir de la 1ère requête. 3 de la publication ont la clé d'exclusion méta et 1 d'entre eux contient la valeur d'exclusion méta.

mysql> SELECT ID FROM wpomni_posts WHERE post_type ='post' AND post_status = 'publish';
+------+
| ID   |
+------+
|    1 |
| 5900 |
| 5904 |
| 5908 |
| 5925 |
+------+
5 rows in set (0.00 sec)
    mysql> SELECT SQL_CALC_FOUND_ROWS  wpomni_posts.ID FROM wpomni_posts  
        -> LEFT JOIN wpomni_postmeta 
        -> ON (wpomni_posts.ID = wpomni_postmeta.post_id 
        -> AND wpomni_postmeta.meta_key = '_wpse_custom_key')
        -> INNER JOIN wpomni_postmeta AS mt1 
        -> ON (wpomni_posts.ID = mt1.post_id) 
        -> WHERE 1=1  
        -> AND wpomni_posts.post_type = 'post' 
        -> AND (wpomni_posts.post_status = 'publish' 
        -> OR wpomni_posts.post_status = 'private') 
        -> AND ( wpomni_postmeta.post_id IS NULL
        -> OR  (mt1.meta_key = '_wpse_custom_key' 
        -> AND CAST(mt1.meta_value AS CHAR) != 'asdf') ) 
        -> GROUP BY wpomni_posts.ID 
        -> ORDER BY wpomni_posts.post_date DESC LIMIT 0, 18;
    +------+
    | ID   |
    +------+
    | 5925 |
    | 5908 |
    | 5900 |
    |    1 |
    +------+
    4 rows in set (0.00 sec)

    mysql> SELECT * FROM wpomni_postmeta WHERE meta_key = '_wpse_custom_key';
    +---------+---------+------------------+----------------+
    | meta_id | post_id | meta_key         | meta_value     |
    +---------+---------+------------------+----------------+
    |  137033 |    5908 | _wpse_custom_key | 1              |
    |  137034 |    5904 | _wpse_custom_key | asdf           |
    |  137042 |    5925 | _wpse_custom_key | what the blank |
    +---------+---------+------------------+----------------+
    3 rows in set (0.00 sec)
0
Chris_O